package com.niodata.dp.task;

import com.niodata.dp.collection.StringList;
import com.niodata.dp.core.exception.InvalidParamException;
import com.niodata.dp.task.entity.PackageDefaultParams;
import com.niodata.dp.task.entity.PackageMetaInfo;
import com.niodata.dp.task.entity.PackageOutParams;
import com.niodata.dp.task.entity.PackageParam;
import com.niodata.dp.task.entity.PackageParams;
import com.niodata.dp.task.entity.PluginPackage;
import com.niodata.dp.task.entity.PluginPackageType;
import com.niodata.dp.util.FileUtil;
import com.niodata.dp.util.JsonUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.zip.ZipFile;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;

public class PluginPackageParser {

  private static Logger logger = Logger.getLogger(PluginPackageParser.class);

  private PluginPackageParser() {
  }

  /**
   * parse job package zip file.
   *
   * @param zipFile package zip file
   * @param tmpdir outputdir
   * @return jobpackage
   * @throws IOException throws io exception
   */
  public static PluginPackage parse(File zipFile, String tmpdir) throws Exception {
    String name = zipFile.getName();
    name = name.substring(0, name.lastIndexOf(".zip"));
    String packageDir = tmpdir + File.separator + name;
    File distFile = new File(packageDir);
    FileUtil.unzip(new ZipFile(zipFile), distFile);
    File metaFile = new File(packageDir + File.separator + "meta.json");
    if (!metaFile.exists()) {
      throw new InvalidParamException("meta.json not exists[" + zipFile.getName() + "]");
    }
    File usageFile = new File(packageDir + File.separator + "doc"
        + File.separator + "usage.md");
    if (!usageFile.exists()) {
      throw new InvalidParamException("usage.md not exists[" + zipFile.getName() + "]");
    }

    //check changeLog
    PackageMetaInfo metaInfo = PluginPackageParser.parseMetaInfo(metaFile);
    String changeLogPath = packageDir + File.separator + "doc" + File.separator
        + metaInfo.getVersion() + "-changeLog.md";
    File changeLogFile = new File(changeLogPath);
    if (!changeLogFile.exists()) {
      throw new InvalidParamException(metaInfo.getVersion() + "-changeLog.md not exists!");
    }

    PluginPackage pluginPackage = new PluginPackage();
    pluginPackage.setPkgName(metaInfo.getName());
    pluginPackage.setPkgVersion(metaInfo.getVersion());
    pluginPackage.setJobType(metaInfo.getJobType());
    pluginPackage.setPkgType(metaInfo.getPkgType());
    PluginPackageType packageType = PluginPackageType.valueOf(pluginPackage.getPkgType());
    if (packageType == null) {
      throw new InvalidParamException("Plugin Pkg Type Error:" + pluginPackage.getPkgType()
          + ",[ETL, FilePipe, ML, FLAG, System]");
    }
    pluginPackage.setDeveloper(metaInfo.getDeveloper());
    pluginPackage.setPkgLang(metaInfo.getLanguage());
    pluginPackage.setDescription(metaInfo.getDescription());
    pluginPackage.setSourceTag(metaInfo.getSourceTag());
    pluginPackage.setSourceRepo(metaInfo.getSourceRepo());
    pluginPackage.setPkgPath(packageDir);
    pluginPackage.setCurrent(true);
    pluginPackage.setMaintainers(metaInfo.getMaintainers());
    pluginPackage.setDependencies(new StringList(metaInfo.getDependencies()));
    String currentChangeLog = getChangeLogs(packageDir + File.separator + "doc");
    pluginPackage.setChanges(currentChangeLog);
    pluginPackage.setScmType(metaInfo.getScmType());
    pluginPackage.setFailRetry(metaInfo.isFailRetry());
    if (metaInfo.getOutParams() != null) {
      PackageOutParams packageOutParams = new PackageOutParams();
      packageOutParams.setParamSchemas(metaInfo.getOutParams());
      pluginPackage.setOutParams(packageOutParams);
    }
    List<PackageParam> paramSchemas =
        JobTypeParamSchemas.getJobTypeParamSchemas(pluginPackage.getJobType(),
            pluginPackage.getPkgLang());
    if (!pluginPackage.isFailRetry()) {
      Iterator<PackageParam> iterator = paramSchemas.iterator();
      while (iterator.hasNext()) {
        PackageParam packageParam = iterator.next();
        if (packageParam.getName().equals("retries")
            || packageParam.getName().equals("retry.backoff")) {
          iterator.remove();
        }
      }
    }
    if (metaInfo.getPkgParams() != null && metaInfo.getPkgParams().size() > 0) {
      paramSchemas.addAll(metaInfo.getPkgParams());
    }
    PackageParams params = new PackageParams();
    params.setParamSchemas(paramSchemas);
    pluginPackage.setParams(params);
    PackageDefaultParams defaultParams = new PackageDefaultParams();
    pluginPackage.setDefaultParams(defaultParams);
    File defaultParam = new File(packageDir + File.separator + "defaultParam.properties");
    if (defaultParam.exists()) {
      Properties properties = new Properties();
      InputStream inputStream = new FileInputStream(defaultParam);
      properties.load(inputStream);
      for (Object key : properties.keySet()) {
        defaultParams.setParam(key.toString(), properties.get(key).toString());
      }
      inputStream.close();
      pluginPackage.getParams().getParamSchemas().forEach(new Consumer<PackageParam>() {
        @Override
        public void accept(PackageParam schema) {
          String defaultValue = defaultParams.getParam(schema.getName());
          if (defaultValue != null) {
            schema.setDefaultValue(defaultValue);
          }
        }
      });
    }
    return pluginPackage;
  }

  private static String getChangeLogs(String changeLogDir) throws Exception {

    StringBuffer buffer = new StringBuffer();
    Map<String, String> treeMap = new TreeMap<>();
    File dir = new File(changeLogDir);
    File[] files = dir.listFiles();
    for (File file : files) {
      if (file.getName().contains("-changeLog.md")) {
        treeMap.put(file.getName().replace("-changeLog.md", ""),
            FileUtils.readFileToString(file, "UTF-8"));
      }
    }

    treeMap = ((TreeMap<String, String>) treeMap).descendingMap();

    Iterator iterator = treeMap.keySet().iterator();
    while (iterator.hasNext()) {
      String key = (String) iterator.next();
      buffer.append("## " + key + "\n");
      buffer.append(treeMap.get(key) + "\n");
    }
    return buffer.toString();
  }

  /**
   * 去掉markdown中的html注释 .
   */
  public static String removeHtmlNote(String str) throws Exception {
    int start = -1;
    while ((start = str.indexOf("<!--")) != -1) {
      start = str.indexOf("<!--");
      int end = str.indexOf("-->");
      if (start != -1 && end == -1) {
        throw new InvalidParamException("parse md error !");
      }
      String str1 = str.substring(0, start);
      String str2 = str.substring(end + 3);
      str = str1.concat(str2);
    }
    return str;
  }

  public static PackageMetaInfo parseMetaInfo(File metaFile) throws IOException {
    byte[] bytes = Files.readAllBytes(metaFile.toPath());
    String json = new String(bytes, "UTF-8");
    return JsonUtil.fromJson(PackageMetaInfo.class, json);
  }

  public static PackageMetaInfo parseMetaInfo(String metaJson) throws IOException {
    return JsonUtil.fromJson(PackageMetaInfo.class, metaJson);
  }

}
